import L, { LatLng, Point } from 'leaflet';

/**
 * 计算给定点到另一条线最短距离的交点
 * @param point 线段两个端点，其中第一个点为首端，第二个为末端
 * @param line 线 多段线
 * @returns { point, sequence, index },point：交点位置，sequence：与第几段线距离最短，index：在lines[sequence]的位置集合中，在第几个index添加个点
 */
export function segmentWithLineCross(point: Point, lines: Point[][]) {
    let minDistance = -1,
        crossPoint = point,
        sequence = 0,
        index = -1;
    lines.forEach((line, sequenceIndex) => {
        for (let i = 0; i < line.length; i++) {
            const nextIndex = i + 1;
            if (nextIndex == line.length) {
                break;
            }

            const current = line[i];
            const next = line[nextIndex];

            if (point.equals(current) || point.equals(next)) {
                // 在同一条线上
                continue;
            }
            // 求垂足
            const footPoint = segmentFootPoint(point, [current, next]);
            if (!footPoint) {
                continue;
            }

            // 计算最小距离的交点
            const distance = pointDistance(point, footPoint);
            if (distance <= minDistance || minDistance == -1) {
                crossPoint = footPoint;
                index = nextIndex;
                minDistance = distance;
                sequence = sequenceIndex;
            }
        }
    });

    return { point: crossPoint, sequence, index };
}

/**
 * 计算点在线段上的垂直投影坐标
 * @param point
 * @param segment
 * @returns
 */
function segmentFootPoint(point: Point, segment: [Point, Point]) {
    const startPoint = segment[0];
    const endPoint = segment[1];

    // 线段本身转成向量
    const vec1 = [endPoint.x - startPoint.x, endPoint.y - startPoint.y];

    // 线段起点与给定点组成向量
    const vec2 = [point.x - startPoint.x, point.y - startPoint.y];

    // 向量点积
    const dot = vec1[0] * vec2[0] + vec1[1] * vec2[1];

    // 小于0说明垂足在线段起点外侧
    if (dot < 0) {
        return startPoint;
    }

    // 计算线段长度
    const distance = pointDistance(startPoint, endPoint);

    // 计算投影长度
    const proj = dot / distance;

    // 线段长度小于投影长度，说明垂足超出线段，在终点另一侧
    if (distance < proj) {
        return endPoint;
    }

    // 垂足在线段上
    const ratio = proj / distance;
    const x = segment[0].x + vec1[0] * ratio;
    const y = segment[0].y + vec1[1] * ratio;

    return L.point(x, y);
}

/**
 * 计算两个线段的交点
 * @param segment1
 * @param segment2
 */
export function segmentCrossPoint(segment1: [Point, Point], segment2: [Point, Point]) {
    const a = segment1[0];
    const b = segment1[1];
    const c = segment2[0];
    const d = segment2[1];

    // 平行
    const denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y);
    if (denominator == 0) {
        return null;
    }

    // 线段所在直线的交点坐标 (x , y)
    const x =
        ((b.x - a.x) * (d.x - c.x) * (c.y - a.y) + (b.y - a.y) * (d.x - c.x) * a.x - (d.y - c.y) * (b.x - a.x) * c.x) /
        denominator;
    const y =
        -((b.y - a.y) * (d.y - c.y) * (c.x - a.x) + (b.x - a.x) * (d.y - c.y) * a.y - (d.x - c.x) * (b.y - a.y) * c.y) /
        denominator;

    return L.point(x, y);
}

/**
 * 两点之间距离
 * @param point1
 * @param point2
 * @returns
 */
export function pointDistance(point1: Point, point2: Point) {
    const x = point1.x - point2.x;
    const y = point1.y - point2.y;
    return Math.sqrt(x * x + y * y);
}

export function isEqualLatlng(latLng1: LatLng, latLng2: LatLng) {
    if (!latLng1 || !latLng2) {
        return false;
    }
    const pow = Math.pow(10, 6);
    if (
        Math.floor(latLng1.lat * pow) / pow === Math.floor(latLng2.lat * pow) / pow &&
        Math.floor(latLng1.lng * pow) / pow === Math.floor(latLng2.lng * pow) / pow
    ) {
        return true;
    } else {
        return false;
    }
}

// 判断点是否在线上
export function pointInSegments(point: Point, line: [Point, Point]) {
    const p1 = line[0];
    const p2 = line[1];

    let k1 = ((p2.y - p1.y) / (p2.x - p1.x)).toFixed(3);
    let k2 = ((point.y - p1.y) / (point.x - p1.x)).toFixed(3);
    let error = Math.abs(Number(k2) - Number(k1));
    if (error - 0.1 <= Number.EPSILON) {
        return true;
    } else {
        return false;
    }
}
